package com.util;


import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * LucenceConvert 工具类
 *
 * @author hutongfu
 * @since 2018-01-08 09:49
 */
public class LucenceUtil {

    /**
     * 索引写对象
     */
    private static IndexWriter indexWriter;

    /**
     * 索引读对象
     */
    private static IndexReader indexReader;

    /**
     * 索引搜索对象
     */
    private static IndexSearcher indexSearcher;

    /**
     * 获取索引写对象
     *
     * @return
     */
    public static IndexWriter getIndexWriter() {
        if (null == indexWriter) {
            try {
                indexWriter = new IndexWriter(LucenceConfig.getDirectory(), LucenceConfig.getIndexWriterConfig());
            } catch (IOException e) {
                if (null != indexWriter) {
                    try {
                        indexWriter.close();
                        indexWriter = null;
                    } catch (IOException e1) {
                        throw new RuntimeException(e1);
                    }
                }
                throw new RuntimeException(e);
            }
        }
        return indexWriter;
    }


    /**
     * 获取索引读对象
     *
     * @return
     */
    public static IndexReader getIndexReader() {
        if (null == indexReader) {
            try {
                indexReader = DirectoryReader.open(LucenceConfig.getDirectory());
            } catch (Exception e) {
                if (null != indexReader) {
                    try {
                        indexReader.close();
                        indexReader = null;
                    } catch (Exception e1) {
                        throw new RuntimeException(e1);
                    }
                }

                throw new RuntimeException(e);
            }
        }
        return indexReader;
    }

    /**
     * 获取索引搜索对象
     *
     * @return
     */
    public static IndexSearcher getIndexSearcher() {
        //不判断indexSearcher  新版本indexSearcher 没有close方法  这个地方应该判断reader是否变化
        // 因为 lucence的锁是以Directory为基准的  这样查询过程中 别人修改了索引会等待 多线程情况下就不会有问题
        if (null == indexReader) {
            indexSearcher = new IndexSearcher(LucenceUtil.getIndexReader());
        }
        return indexSearcher;
    }

    /**
     * 关闭reader writer search
     */
    public static void indexChanged() {
        if (null != indexReader) {
            try {
                indexReader.close();
                indexReader = null;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        if (null != indexWriter) {
            try {
                indexWriter.commit();
                indexWriter.close();
                indexWriter = null;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 添加索引
     *
     * @param docs 多个或单个 文档
     */
    public static void addIndex(Document... docs) {
        if (docs.length == 0) {
            return;
        }
        try {
            IndexWriter indexWriter = LucenceUtil.getIndexWriter();
            List<Document> list = Arrays.asList(docs);
            list.forEach(doc -> {
                try {
                    indexWriter.addDocument(doc);
                } catch (IOException e) {
                    System.out.println("添加索引失败...");
                    throw new RuntimeException(e);
                }
            });
            LucenceUtil.indexChanged();
            System.out.println("添加索引成功");
        } catch (Exception e) {
            System.out.println("添加索引失败...");
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除所有索引
     */
    public static void delAllIndex() {
        try {
            IndexWriter indexWriter = LucenceUtil.getIndexWriter();
            indexWriter.deleteAll();
            LucenceUtil.indexChanged();
            System.out.println("删除全部索引成功");
        } catch (Exception e) {
            System.out.println("删除全部索引失败...");
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据 “词” 删除索引
     *
     * @param term 单个 “词”
     */
    public static void delIndexByTerm(Term... term) {
        try {
            IndexWriter indexWriter = LucenceUtil.getIndexWriter();
            indexWriter.deleteDocuments(term);
            LucenceUtil.indexChanged();
            System.out.println("删除索引成功");
        } catch (Exception e) {
            System.out.println("删除索引失败...");
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新索引
     * 一般根据id，Term
     *
     * @param term   词
     * @param newDoc 新文档
     */
    public static void updateIndexByTerm(Term term, Document newDoc) {
        try {
            IndexWriter indexWriter = LucenceUtil.getIndexWriter();
            indexWriter.updateDocument(term, newDoc);
            LucenceUtil.indexChanged();
            System.out.println("更新索引成功");
        } catch (Exception e) {
            System.out.println("更新索引失败...");
            throw new RuntimeException(e);
        }
    }

    /**
     * 分页查询
     *
     * @param page           分页对象
     * @param query          查询对象
     * @param lucenceConvert 自定义转换对象接口
     * @param <T>
     * @return
     */
    public static <T> LucencePage<T> findPage(LucencePage<T> page, Query query, LucenceConvert<T> lucenceConvert) {
        IndexSearcher indexSearcher = LucenceUtil.getIndexSearcher();
        //查询前n条
        int n = page.getFirstResult() + page.getPageSize();
        try {
            TopDocs topDocs = indexSearcher.search(query, n);
            int total = topDocs.totalHits;
            page.setTotal(total);
            //会延迟加载
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            //分页
            int endIndex = Math.min(n, scoreDocs.length);
            List<T> list = new ArrayList<>();

            for (int i = page.getFirstResult(); i < endIndex; i++) {
                int docId = scoreDocs[i].doc;
                Document doc = indexSearcher.doc(docId);
                T t = lucenceConvert.documentToObj(doc);
                list.add(t);
            }
            page.setData(list);
            System.out.printf("一共匹配到：%d,实际取出：%d", total, scoreDocs.length);
        } catch (Exception e) {
            System.out.println("索引匹配失败...");
            throw new RuntimeException(e);
        }
        return page;
    }


    /**
     * 查询所有
     *
     * @param query
     * @param lucenceConvert
     * @param <T>
     * @return
     */
    public static <T> List<T> findAll(Query query, LucenceConvert<T> lucenceConvert) {
        LucencePage<T> page = new LucencePage<>(1, Integer.MAX_VALUE);
        return findPage(page, query, lucenceConvert).getData();
    }

}
